<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>flappy_bird</title>
    <style>
        canvas {
            border:1px solid red;
        }
        img {
            display: none;
        }
    </style>
</head>
<body>
    <canvas id="cvs" width="500" height="500"></canvas>
    <script src="./loadImage.js"></script>
    <script>
        function extend( o1, o2 ) {
            for ( var key in o2 ) {
                if ( o2.hasOwnProperty( key ) ) {
                    o1[ key ] = o2[ key ];
                }
            }
        }
    </script>
    <script>
        var cvs = document.getElementById('cvs');
        var ctx = cvs.getContext('2d');

        /*
         * 绘制背景
         * construcotor { Sky } 背景构造函数
         * parasm { ctx: Context } 绘制环境
         * parasm { img: Image } 背景图像
         * parasm { speed: number } 背景速度
         * */
        function Sky( ctx, img, speed ) {

            this.ctx = ctx;
            this.img = img;
            this.width = this.img.width;
            this.height = this.img.height;
            this.speed = speed || 2;

            // 每创建一个实例，len自增
            Sky.len++;

            this.x = this.width * ( Sky.len - 1 );
            this.y = 0;
        }

        // 背景实例的个数
        Sky.len = 0;


        // 给原型扩充方法
        Sky.prototype = {

            constructor: Sky,

            // 绘制背景
            draw: function() {
                this.ctx.drawImage( this.img, this.x, this.y );
            },

            update: function() {
                this.x -= this.speed;
                if ( this.x <= -this.width ) {
                    this.x += this.width * Sky.len;
                }
            }

        };
    </script>
    <script>

        /*
        * constructor { Bird } 鸟构造函数
        * param { ctx: Context } 绘图环境
        * param { img: Image } 鸟图
        * param { widthFrame: number } 一排的帧数
        * param { heightFrame: number } 一列的帧数
        * param { x: number } 鸟的起始x轴坐标
        * param { y: number } 鸟的起始y轴坐标
        * */
        function Bird( ctx, img, widthFrame, heightFrame, x, y ) {

            this.ctx = ctx;
            this.img = img;
            this.widthFrame = widthFrame;
            this.heightFrame = heightFrame;
            this.x = x;
            this.y = y;

            // 一个小鸟的宽和高
            this.width = this.img.width / this.widthFrame;
            this.height = this.img.height / this.heightFrame;

            // 当前小鸟渲染的帧数
            this.currentFrame = 0;

            // 小鸟的下落速度
            this.speed = 2;

            // 加速度
            this.speedPlus = 0.8;

            // 绑定事件
            this._bind();
        }

        // 给原型扩展方法
        Bird.prototype = {

            constructor: Bird,

            // 绘制鸟
            draw: function() {

                // 当下落速度为1的时候，旋转10度
                var baseRadian = Math.PI / 180 * 10;
                var maxRadian = Math.PI / 180 * 45;

                // 根据速度计算旋转的弧度
                var rotateRadian = baseRadian * this.speed;

                // 限制最大旋转角度为70度
                rotateRadian = rotateRadian >= maxRadian? maxRadian : rotateRadian;

                this.ctx.save();

                /*
                * 1、平移到小鸟的中心点
                * 2、然后根据下落的速度旋转坐标系
                * 3、绘制小鸟，但是绘制的x和y坐标变为负的宽高一半。
                * */

                this.ctx.translate( this.x + this.width / 2, this.y + this.height / 2 );
                this.ctx.rotate( rotateRadian );
                this.ctx.drawImage( this.img,
                    this.width * this.currentFrame, 0, this.width, this.height,
                    -this.width / 2, -this.height / 2, this.width, this.height );

                this.ctx.restore();
            },

            // 计算下一帧绘制时的数据
            update: function() {

                // 绘制下一帧
                this.currentFrame = ++this.currentFrame >= this.widthFrame? 0 : this.currentFrame;

                // 让小鸟不断下落
                this.y += this.speed;

                // 刷新下落数度
                this.speed += this.speedPlus;

            },

            // 绑定事件
            _bind: function() {

                var self = this;

                this.ctx.canvas.addEventListener( 'click', function() {

                    self.speed = -3;

                } );
            }
        }

    </script>
    <script>
        /*
        * constrcutor { Land } 大地
        * param { ctx: Context } 绘图环境
        * param { img: Image } 绘制的图像资源
        * param { speed: number } 速度
        * */
        function Land( ctx, img, speed ) {

            this.ctx = ctx;
            this.img = img;
            this.speed = speed || 2;

            Land.len++;

            this.x = this.img.width * ( Land.len - 1 );
            this.y = this.ctx.canvas.height - this.img.height;
        }

        // 大地实例的数量
        Land.len = 0;

        extend( Land.prototype, {

            draw: function() {
                this.ctx.drawImage( this.img, this.x, this.y );
            },

            update: function() {
                this.x -= this.speed;
                this.x += this.x <= -this.img.width? this.img.width * Land.len: 0;
            }

        } );
    </script>
    <script>
        /*
        * 管道的特点：
        * 1、成对出现，所以x轴可以共享，但是y轴不共享
        * 2、上下管道之间的路径固定，可以由用户指定
        * 3、管道的高度是随机生成的，随机生成上管道的高度，下管道就可以计算了
        * 4、当管道走出画布，从右边出来时，高度需要重新随机生成
        * */

        /*
        * constructor { Pipe } 管道
        * param { ctx: Context } 绘图环境
        * param { imgDown：Image } 口朝下的管道，在画布的上面
        * param { imgUp：Image } 口朝上的管道，在画布的下面
        * param { space：number } 上下管道的间距
        * param { landHeight：number } 大地的高度
        * param { speed：number } 速度
        * */
        function Pipe( ctx, imgDown, imgUp, space, landHeight, speed ) {

            this.ctx = ctx;
            this.imgDown = imgDown;
            this.imgUp = imgUp;
            this.space = space;
            this.landHeight = landHeight;
            this.speed = speed;

            // 管道最小高度
            this.minHeight = 100;

            // 管道默认的宽高
            this.width = this.imgDown.width;
            this.height = this.imgDown.height;

            Pipe.len++;

            this.x = 300 + this.width * 3 * ( Pipe.len - 1 );
            this.y = 0;

            // 初始化管道的坐标
            this._init();
        }

        // 管道实例的数量
        Pipe.len = 0;

        // 扩展原型方法
        extend( Pipe.prototype, {

            // 初始化管道的坐标
            _init: function() {

                // 单个管道的最大高度
                var maxHeight = this.ctx.canvas.height - this.landHeight - this.space - this.minHeight;

                // 随机生成上管道的高度在 50 到 maxHeight 之间
                var randomHeight = Math.random() * maxHeight;
                randomHeight = randomHeight < this.minHeight? this.minHeight : randomHeight;

                // 上面管道的y轴坐标 = 随机生成的高度 - 管道默认的高度
                this.downY = randomHeight - this.height;

                // 下面管道的y轴坐标 = 随机生成的高度 + 上下管道的间隔
                this.upY = randomHeight + this.space;
            },

            // 绘制管道
            draw: function() {
                this.ctx.drawImage( this.imgDown, this.x, this.downY );
                this.ctx.drawImage( this.imgUp, this.x, this.upY );
                this._drawPath();
            },

            // 根据管道的宽高和坐标绘制对应的路径
            _drawPath: function() {
                this.ctx.rect( this.x, this.downY, this.width, this.height );
                this.ctx.rect( this.x, this.upY, this.width, this.height );
                this.ctx.stroke();
            },

            // 更新下一帧的数据
            update: function() {

                this.x -= this.speed;

                // 管道走出画布，向右拼接，同时重新生成高度
                if ( this.x <= -this.width ) {
                    this._init();
                    this.x += this.width * 3 * Pipe.len;
                }

            }

        } );
    </script>
    <script>

        loadImage({
            bird: './img/bird.png',
            land: './img/land.png',
            pipeDown: './img/pipeDown.png',
            pipeUp: './img/pipeUp.png',
            sky: './img/sky.png'
        }, function( imgObj ) {

            // 根据背景的大小设置画布的大小
            cvs.width = imgObj.sky.width;
            cvs.height = imgObj.sky.height;

            // 为了实现背景无限轮播，所以需要两块背景
            var sky = new Sky( ctx, imgObj.sky, 3 );
            var sky2 = new Sky( ctx, imgObj.sky, 3 );

            // 大地
            var land = new Land( ctx, imgObj.land, 3 );
            var land1 = new Land( ctx, imgObj.land, 3 );
            var land2 = new Land( ctx, imgObj.land, 3 );
            var land3 = new Land( ctx, imgObj.land, 3 );

            // 管道
            var pipe = new Pipe( ctx, imgObj.pipeDown, imgObj.pipeUp, 150, imgObj.land.height, 3 );
            var pipe1 = new Pipe( ctx, imgObj.pipeDown, imgObj.pipeUp, 150, imgObj.land.height, 3 );
            var pipe2 = new Pipe( ctx, imgObj.pipeDown, imgObj.pipeUp, 150, imgObj.land.height, 3 );
            var pipe3 = new Pipe( ctx, imgObj.pipeDown, imgObj.pipeUp, 150, imgObj.land.height, 3 );
            var pipe4 = new Pipe( ctx, imgObj.pipeDown, imgObj.pipeUp, 150, imgObj.land.height, 3 );
            var pipe5 = new Pipe( ctx, imgObj.pipeDown, imgObj.pipeUp, 150, imgObj.land.height, 3 );

            // 创建鸟
            var bird = new Bird( ctx, imgObj.bird, 3, 1, 10, 10 );

            // 让背景动起来
            var timer = setInterval(function() {

                /*
                * 每次绘制新的游戏画面时，
                * 先判断小鸟有没有碰撞，
                * 如果碰撞暂停定时器。
                * */
                var birdCoreX = bird.x + bird.width / 2;
                var birdCoreY = bird.y + bird.height / 2;

                // 如果小鸟撞向管道，或者飞出天空，或者duang~duang~duang，那么游戏结束
                if ( ctx.isPointInPath( birdCoreX, birdCoreY )
                        || birdCoreY < 0
                        || birdCoreY > (ctx.canvas.height - imgObj.land.height) )
                {
                    clearInterval( timer );
                    ctx.fillStyle = 'rgba( 100, 100, 100, 0.8 )';
                    ctx.fillRect( 0, 0, ctx.canvas.width, ctx.canvas.height );
                    ctx.textAlign = 'center';
                    ctx.textBaseline = 'middle';
                    ctx.fillStyle = 'red';
                    ctx.font = '900 40px 微软雅黑';
                    ctx.fillText( 'GAME OVER!!!', ctx.canvas.width / 2, ctx.canvas.height / 2 );
                    return;
                }

                sky.draw();
                sky.update();

                sky2.draw();
                sky2.update();

                // 先清除上一次绘制的6个管道路径，
                // 然后再按照新的位置绘制新路径
                ctx.beginPath();

                pipe.draw();
                pipe.update();

                pipe1.draw();
                pipe1.update();

                pipe2.draw();
                pipe2.update();

                pipe3.draw();
                pipe3.update();

                pipe4.draw();
                pipe4.update();

                pipe5.draw();
                pipe5.update();

                land.draw();
                land.update();

                land1.draw();
                land1.update();

                land2.draw();
                land2.update();

                land3.draw();
                land3.update();

                bird.draw();
                bird.update();
            }, 50);

        });
    </script>
</body>
</html>